/*
 * device.cpp
 *
 *  Created on: 28.06.2011
 *      Author: marcell
 */

#include <assert.h>
#include <ifile.h>
#include <gfx/material.h>
#include <gfx/light.h>
#include <gfx/gl20/device.h>
#include <gfx/gl20/effect_gl20.h>
#include <gfx/gl20/effecttechnique_gl20.h>
#include <gfx/gl20/glchecker.h>
#include <gfx/gl20/indexbuffer_gl15.h>
#include <gfx/gl20/program_gl20.h>
#include <gfx/gl20/rendertargetfbo.h>
#include <gfx/gl20/shader_gl20.h>
#include <gfx/gl20/texture.h>
#include <gfx/gl20/vertexbuffer_gl15.h>
#include <gfx/gl20/vertexdeclaration_gl13.h>
#include <gfx/gl20/mesh/billboardmesh.h>
#include <gfx/gl20/mesh/instancedmeshgpu.h>
#include <gfx/gl20/mesh/instancedmeshsw.h>
#include <gfx/gl20/mesh/morphmesh.h>
#include <gfx/gl20/mesh/patchmesh.h>
#include <gfx/gl20/mesh/skinmeshgpu.h>
#include <gfx/gl20/mesh/staticmesh.h>
#include <sharedvaluemanager.h>
#include <math.h>
PFNGLDRAWELEMENTSINSTANCEDPROC ceDrawElementsInstanced;


#define CE_CHECK_GL_ERROR \
{\
  GLenum error = glGetError ();\
  if (error != GL_NO_ERROR)\
    {\
      printf ("GL_ERROR: 0x%08x\n", error);\
      printf ("Error   : %s\n", (const char*)gluErrorString(error));\
      fflush (stdout);\
    }\
  assert(error == GL_NO_ERROR);\
}



ceDeviceGL20::ceDeviceGL20 ()
  : _capable (false)
  , _indices (0)
  , _vertexDeclaration (0)
  , _program (0)
	, _effectTechnique (0)
  , _renderTarget (0)
  , _defaultAnisotropic (1)
  , _defaultMipMap (true)
  , _defaultFilter (F_Trilinear)
  , _defaultWrapMode (WM_Repeat)
  , _shadowOverlayer(0)
  , _useBlend (false)
{
  glewInit ();
  checkCapabilities ();

  glPixelStorei (GL_UNPACK_ALIGNMENT, 1);

  for (unsigned i = 0; i < MaxStreams; i++)
		{
			_vertexStreams[i] = 0;
		}
  for (unsigned i = 0; i < TS_Count; i++)
		{
			_textures[i] = 0;
		}
	_shadowMap[0].ShadowMap = 0;
	_shadowMap[1].ShadowMap = 0;
	_shadowMap[2].ShadowMap = 0;

  _light = 0;

  glMatrixMode (GL_MODELVIEW);
  while (glGetError() != GL_STACK_UNDERFLOW && glGetError() != GL_STACK_OVERFLOW)
    {
      glPopMatrix();
    }
  glPushMatrix();


	_shadowBias = ceMatrix4f (0.5f, 0.0f, 0.0f, 0.0f,
													 0.0f, 0.5f, 0.0f, 0.0f,
													 0.0f, 0.0f, 0.5f, 0.0f,
													 0.5f, 0.5f, 0.495f, 1.0f);
}

void ceDeviceGL20::checkCapabilities ()
{
  unsigned version = get_opengl_major_version ();
  _capable = version >= OPENGL_VERSION(2,1);

  DebugStats ();

  unsigned glslMajor, glslMinor;
  sscanf((const char*)glGetString (GL_SHADING_LANGUAGE_VERSION), "%d.%d", &glslMajor, &glslMinor);
  _glslVersion = glslMajor * 100 + glslMinor;
  
  if (glDrawElementsInstanced)
    {
      ceDrawElementsInstanced = glDrawElementsInstanced;
    }
  else
    {
      ceDrawElementsInstanced = glDrawElementsInstancedARB;
    }
  _caps.HardwareInstancing = ceDrawElementsInstanced != 0;

}

void ceDeviceGL20::DebugStats()
{
  glGetIntegerv(GL_MAX_TEXTURE_COORDS, (GLint*)&_maxTextureCoordinates);
  glGetIntegerv(GL_MAX_TEXTURE_IMAGE_UNITS, (GLint*)&_maxTextureUnits);
  glGetIntegerv(GL_MAX_TEXTURE_SIZE,  (GLint*)&_maxTextureSize);

  printf ("\nOpenGL-Specification:\n");
  printf ("Vendor                 : %s\n", glGetString (GL_VENDOR));
  printf ("Renderer               : %s\n", glGetString (GL_RENDERER));
  printf ("Version                : %s\n", glGetString (GL_VERSION));
  printf ("GLSL-Version           : %s\n", glGetString (GL_SHADING_LANGUAGE_VERSION));
  printf ("Max texture coordinates: %d\n", _maxTextureCoordinates);
  printf ("Max textures           : %d @ %dx%d\n",
          _maxTextureUnits, _maxTextureSize, _maxTextureSize);

  fflush(stdout);

}

const ceCapabilities& ceDeviceGL20::GetCapabilities()
{
  return _caps;
}

bool ceDeviceGL20::IsCapable ()
{
  return _capable;
}


bool ceDeviceGL20::CheckShadingVersion (unsigned version)
{
  return version <= _glslVersion;
}

bool ceDeviceGL20::CheckExtension (const char* extension)
{
  return glewIsExtensionSupported (extension);
}

ceRef<iVertexBuffer> ceDeviceGL20::CreateVertexBuffer (unsigned size, const void* data, ceBufferDataMode mode)
{
  ceVertexBufferGL15* vb = new ceVertexBufferGL15 ();
  if (!vb->CreateBuffer (size, data, mode))
  {
    delete vb;
    return 0;
  }

  return vb;
}

ceRef<iIndexBuffer> ceDeviceGL20::CreateIndexBuffer (unsigned size, const void* data, ceBufferDataMode mode)
{
  ceIndexBufferGL15* ib = new ceIndexBufferGL15 ();
  if (!ib->CreateBuffer (size, data, mode))
  {
    delete ib;
    return 0;
  }

  return ib;
}

ceRef<iEffect> ceDeviceGL20::CreateEffect ()
{
	return new ceEffectGL20 ();
}

ceRef<iEffectTechnique> ceDeviceGL20::CreateEffectProgram()
{
	return new ceEffectTechniqueGL20 ();
}

ceRef<iProgram> ceDeviceGL20::CreateProgram ()
{
  return new ceProgramGL20 ();
}


ceRef<iShader> ceDeviceGL20::CreateShader (ceShaderType type) const
{
	iShader *shader = new ceShaderGL20 (type);

	return shader;
}


iTexture2D* ceDeviceGL20::CreateTexture2D (const ceTextureDescriptor& desc)
{
  ceTexture2D* txt = new ceTexture2D ();
  txt->Create (desc);
  return txt;
}

iRenderTarget* ceDeviceGL20::CreateRenderTarget(const ceRenderTargetDescriptor &desc)
{
  ceRenderTargetFBO* fbo = new ceRenderTargetFBO();
  if (!fbo->Initialize(this, desc))
    {
      delete fbo;
      fbo = 0;
    }
  return fbo;
}



iBillboardMesh* ceDeviceGL20::CreateBillboardMesh()
{
  ceBillboardMeshGL20* mesh = new ceBillboardMeshGL20();
  mesh->Initialize(this);
  return mesh;
}

iInstancedMesh* ceDeviceGL20::CreateInstancedMesh()
{
  if (_caps.HardwareInstancing)
    {
      return new ceInstancedMeshGPUGL20 ();
    }
  else
    {
      return new ceInstancedMeshSWGL20 ();
    }
}

iMorphMesh* ceDeviceGL20::CreateMorphMesh ()
{
  return new ceMorphMeshGL20 ();
}

iPatchMesh* ceDeviceGL20::CreatePatchMesh()
{
  return new cePatchMeshGL20 ();
}

iSkinMesh* ceDeviceGL20::CreateSkinMesh ()
{
  return new ceSkinMeshGPUGL20 ();
}

iStaticMesh* ceDeviceGL20::CreateStaticMesh()
{
  return new ceStaticMeshGL20 ();
}


void ceDeviceGL20::SetDefaultAnisotropic (unsigned anisotropic)
{
  _defaultAnisotropic = anisotropic;
}

unsigned ceDeviceGL20::GetDefaultAnisotropic () const
{
  return _defaultAnisotropic;
}

void ceDeviceGL20::SetDefaultMipMap (bool mipMap)
{
  _defaultMipMap = mipMap;
}

bool ceDeviceGL20::IsDefaultMipMap () const
{
  return _defaultMipMap;
}

void ceDeviceGL20::SetDefaultFilter (ceFilter filter)
{
  _defaultFilter = filter;
}

ceFilter ceDeviceGL20::GetDefaultFilter () const
{
  return _defaultFilter;
}

void ceDeviceGL20::SetDefaultWrapMode(ceWrapMode wrapMode)
{
  _defaultWrapMode = wrapMode;
}

ceWrapMode ceDeviceGL20::GetDefaultWrapMode() const
{
  return _defaultWrapMode;
}


void ceDeviceGL20::SetEffectTechnique(iEffectTechnique* technique)
{
	_effectTechnique = technique;
}

iEffectTechnique* ceDeviceGL20::GetEffectTechnique() const
{
	return _effectTechnique;
}

void ceDeviceGL20::SetProgram (iProgram* program)
{
	_program = program;
}

iProgram* ceDeviceGL20::GetProgram() const
{
  return _program;
}

void ceDeviceGL20::ClearShadowMap (unsigned i)
{
	assert (i < 3);
	_shadowMap[i].ShadowMap = 0;
}

void ceDeviceGL20::SetShadowMap (unsigned i,
														 iTexture* shadowMap, 
														 const ceMatrix4f& viewMatrix,
														 const ceMatrix4f& projectionMatrix)
{
	assert (i < 3);
	_shadowMap[i].Matrix						= _shadowBias * projectionMatrix * viewMatrix;
	_shadowMap[i].ShadowMap					= shadowMap;
}

void ceDeviceGL20::SetShadowOverlayer(iTexture *shadowOverlayer)
{
  _shadowOverlayer = shadowOverlayer;
}

void ceDeviceGL20::SetUseBlend(bool useBlend)
{
  _useBlend = useBlend;
}

void ceDeviceGL20::SetMaterial (ceMaterialSpec *material)
{
  _material = material;
}


void ceDeviceGL20::SetLight (ceLight *light)
{
  _light = light;
}


void ceDeviceGL20::ClearTextures ()
{
	for (unsigned i=0; i<TS_Count; i++)
		{
			_textures[i] = 0;
		}
}

void ceDeviceGL20::SetTexture (ceTextureStage stage, iTexture *texture)
{
  _textures[stage] = texture;
}

iTexture* ceDeviceGL20::GetTexture (ceTextureStage stage)
{
  return _textures[stage];
}

void ceDeviceGL20::SetProjectionMatrix(const ceMatrix4f& matrix)
{
  _matrices[MS_Projection] = matrix;
}

void ceDeviceGL20::SetViewMatrix(const ceMatrix4f &matrix)
{
  _matrices[MS_View] = matrix;
}

void ceDeviceGL20::SetModelMatrix(const ceMatrix4f &matrix)
{
  _matrices[MS_World] = matrix;
}

bool ceDeviceGL20::BeginPass (ceRenderPass pass)
{
	if (!_effectTechnique)
    {
      _program = 0;
      glUseProgram(0);
      return false;
    }

	_program = _effectTechnique->GetProgram(pass);
  if (!_program)
    {
      glUseProgram(0);
      return false;
    }

  _program->Bind ();

	_pass = pass;
	BindMatrices ();
	return true;
}

void ceDeviceGL20::BindMatrices ()
{

  iProgramParameter* pp = 0;

  pp = _program->GetParameter(PPB_MatrixProjection);
  if (pp)
    {
      pp->Bind(_matrices[MS_Projection]);
    }
  else
    {
      // the default matrix mode is modelview so set this to projection
      glMatrixMode(GL_PROJECTION);

      // we don't use the matrix stack in projection mode
      glLoadMatrixf((const GLfloat*)(&_matrices[MS_Projection]._00));

      // reset to modelview
      glMatrixMode(GL_MODELVIEW);
    }

  pp = _program->GetParameter(PPB_MatrixView);
  if (pp)
    {
      pp->Bind(_matrices[MS_View]);
    }
  else
    {
      // the default matrix mode is the modelview mode
      // so no need setting a different mode
      // the default state of the matrix stack is
      //  - camera applied
      //  - currently in the matrix stack right behind
      //    the camare matrix (which is the first)
      glPopMatrix();
      // no the top of the stack is the camera matrix
      // so reset it
      glLoadMatrixf((const GLfloat*)(&_matrices[MS_View]._00));
      // and push it so the model matrix is at the top of stack
      glPushMatrix();

    }
}

void ceDeviceGL20::EndPass (ceRenderPass pass)
{
  _program = 0;
  glUseProgram(0);
}

ceRef<iVertexDeclaration> ceDeviceGL20::CreateVertexDeclaration (const ceVertexElement *elements)
{
  return new ceVertexDeclaration (elements);
}

void ceDeviceGL20::SetRenderTarget(iRenderTarget *renderTarget)
{
  if (  _renderTarget != 0
      && renderTarget != _renderTarget
      && _renderTarget->GetMethod() == RTM_Copy)
    {
      // TODO: Copy the current framebuffer into the previously bound rendertarget
    }

  if (renderTarget && renderTarget->GetMethod() == RTM_FBO)
    {
      renderTarget->Bind ();
    }
  else if (_renderTarget && _renderTarget->GetMethod() == RTM_FBO)
    {
      _renderTarget->Unbind();
    }
  _renderTarget = renderTarget;
}

void ceDeviceGL20::SetVertexDeclaration (iVertexDeclaration *vertexDeclaration)
{
  _vertexDeclaration = static_cast<ceVertexDeclaration*> (vertexDeclaration);
}

void ceDeviceGL20::SetStreamSource (unsigned stream, iVertexBuffer *buffer)
{
  assert(stream < MaxStreams);
  _vertexStreams[stream] = buffer;
}

void ceDeviceGL20::SetIndices (iIndexBuffer *indices)
{
  _indices = indices;
}

namespace
{
GLenum convert_primitive_type (cePrimitiveType type)
{
  switch (type)
    {
    case PT_Lines:
      return GL_LINES;
    case PT_Points:
      return GL_POINTS;
    case PT_Triangles:
      return GL_TRIANGLES;
    default:
      break;
    }
  assert(false);
  return GL_TRIANGLES;
}

GLenum convert_data_type (ceDataType type)
{
  switch (type)
    {
    case DT_Byte:
      return GL_BYTE;
    case DT_UnsignedByte:
      return GL_UNSIGNED_BYTE;
    case DT_Short:
      return GL_SHORT;
    case DT_UnsignedShort:
      return GL_UNSIGNED_SHORT;
    case DT_Int:
      return GL_INT;
    case DT_UnsignedInt:
      return GL_UNSIGNED_INT;
    case DT_Float:
      return GL_FLOAT;
    case DT_Double:
      return GL_DOUBLE;
    default:
      break;
    }
  assert(false);
  return GL_UNSIGNED_BYTE;
}

inline ceProgramParameterBinding convert_program_parameter_binding (int ts)
{
  return (ceProgramParameterBinding)((int)PPB_Texture0 + ts);
}


inline void bind_material (iProgram* prog, ceMaterialSpec* mat)
{
  if (!mat) return;

  CE_CHECK_GL_ERROR;
  iProgramParameter *pp = 0;

  pp = prog->GetParameter(PPB_MaterialAmbient);
  if (pp) pp->Bind(mat->Ambient);
  CE_CHECK_GL_ERROR;

  pp = prog->GetParameter(PPB_MaterialDiffuse);
  if (pp) pp->Bind(mat->Diffuse);
  CE_CHECK_GL_ERROR;

  pp = prog->GetParameter(PPB_MaterialSpecular);
  if (pp) pp->Bind(mat->Specular);
  CE_CHECK_GL_ERROR;

  pp = prog->GetParameter(PPB_MaterialShininess);
  if (pp) pp->Bind(mat->Shininess);
  CE_CHECK_GL_ERROR;

  pp = prog->GetParameter(PPB_MaterialEmission);
  if (pp) pp->Bind(mat->Emission);
  CE_CHECK_GL_ERROR;

}

}

void ceDeviceGL20::ApplyShaderStates (ceSharedShaderBinding* bindings)
{
  assert (_program);
  CE_CHECK_GL_ERROR;

  iProgramParameter *pp = _program->GetParameter(PPB_MatrixWorld);
  if (pp)
    {
      pp->Bind(_matrices[MS_World]);
    }



	if (_light)
		{
			_light->Bind(_program);
		}
  bind_material(_program, _material);

	if (bindings)
		{
			bindings->Bind (ceSharedValueManager::Get (), _program);
		}

	if (_material->Transparency)
		{
			glEnable (GL_BLEND);
			glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
		}
	else
		{
      if (_useBlend)
        {
          glEnable(GL_BLEND);
        }
      else
        {
          glDisable(GL_BLEND);
        }
		}

	glDepthMask (_material->WriteDepth);

  glEnable (GL_VERTEX_PROGRAM_POINT_SIZE);
  glEnable (GL_PROGRAM_POINT_SIZE);


  if (_pass == RP_Shadow)
		{
			for (unsigned i=0; i<3; i++)
				{
					if (_shadowMap[i].ShadowMap)
						{
							SetTexture ((ceTextureStage)(TS_Texture5 + i), _shadowMap[i].ShadowMap);
							pp = _program->GetParameter((ceProgramParameterBinding)(PPB_ShadowMap0Matrix + i));
							if (pp)
								{
									pp->Bind (_shadowMap[i].Matrix);
								}
						}
				}
		}
  else if (_pass == RP_Diffuse)
    {
      if (_shadowOverlayer)
        {
          SetTexture(TS_Texture5, _shadowOverlayer);
        }
    }


  for (unsigned i = 0; i < TS_Count; i++)
    {
      iTexture* texture = _textures[i];
      if (texture)
        {
          switch (texture->GetTextureType ())
            {
            case TT_Texture2D:
              {
                glActiveTexture (GL_TEXTURE0 + i);
                texture->Bind();

                iProgramParameter* parameter = _program->GetParameter(convert_program_parameter_binding(i));
                if (parameter)
                  {
                    parameter->Bind((ceTextureStage)i, (iTexture2D*)texture);
                  }
              }
              break;
            default:
              break;
            }
        }
    }
}

GLenum prim_type;
unsigned prim_first;
unsigned prim_count;
GLenum index_type;

void ceDeviceGL20::DrawPrimitives (cePrimitiveType type, unsigned first, unsigned count, bool unbind)
{
  assert(_vertexDeclaration);

  for (unsigned i = 0, j = _vertexDeclaration->GetNumberOfStreams (); i < j; i++)
    {
      void* ptr = _vertexStreams[i]->Bind ();
      _vertexDeclaration->BindStream (_program, i, ptr);
    }

  prim_type = convert_primitive_type (type);
  prim_first = first;
  prim_count = count;
  glDrawArrays (prim_type, prim_first, prim_count);

  if (unbind)
    {
      for (unsigned i = 0, j = _vertexDeclaration->GetNumberOfStreams (); i < j; i++)
        {
          _vertexDeclaration->UnbindStream (_program, i);
        }
    }

}

void ceDeviceGL20::DrawIndexPrimitives (cePrimitiveType pType, ceDataType iType, unsigned count, bool unbind)
{
  assert(_vertexDeclaration);
  assert(_indices);

  for (unsigned i = 0, j = _vertexDeclaration->GetNumberOfStreams (); i < j; i++)
    {
      void* ptr = _vertexStreams[i]->Bind ();
      _vertexDeclaration->BindStream (_program, i, ptr);
    }

  _indices->Bind ();
  prim_type = convert_primitive_type (pType);
  prim_count = count;
  index_type = convert_data_type (iType);
  glDrawElements (prim_type, prim_count, index_type, 0);

  if (unbind)
    {
      for (unsigned i = 0, j = _vertexDeclaration->GetNumberOfStreams (); i < j; i++)
        {
          _vertexDeclaration->UnbindStream (_program, i);
        }
    }
}

void ceDeviceGL20::DrawIndexPrimitivesInstanced (cePrimitiveType pType, ceDataType iType, unsigned count, unsigned instances, bool unbind)
{
  assert(_vertexDeclaration);
  assert(_indices);

  for (unsigned i = 0, j = _vertexDeclaration->GetNumberOfStreams (); i < j; i++)
    {
      void* ptr = _vertexStreams[i]->Bind ();
      _vertexDeclaration->BindStream (_program, i, ptr);
    }

  _indices->Bind ();
  prim_type = convert_primitive_type (pType);
  prim_count = count;
  index_type = convert_data_type (iType);
  ceDrawElementsInstanced (prim_type, prim_count, index_type, 0, instances);
  if (unbind)
    {
      for (unsigned i = 0, j = _vertexDeclaration->GetNumberOfStreams (); i < j; i++)
        {
          _vertexDeclaration->UnbindStream (_program, i);
        }
    }
}


void ceDeviceGL20::RedrawPrimitives (bool unbind)
{
  glDrawArrays (prim_type, prim_first, prim_count);
  if (unbind)
    {
      for (unsigned i = 0, j = _vertexDeclaration->GetNumberOfStreams (); i < j; i++)
        {
          _vertexDeclaration->UnbindStream (_program, i);
        }
    }

}

void ceDeviceGL20::RedrawIndexPrimitives (bool unbind)
{
  glDrawElements (prim_type, prim_count, index_type, 0);
  if (unbind)
    {
      for (unsigned i = 0, j = _vertexDeclaration->GetNumberOfStreams (); i < j; i++)
        {
          _vertexDeclaration->UnbindStream (_program, i);
        }
    }

}

void ceDeviceGL20::DrawRect(const ceRect &rect)
{
  glDisable(GL_CULL_FACE);
  ApplyShaderStates();

#define VERT(u, v) glTexCoord2f(u, v); glVertex3f(rect.x + u*rect.w, rect.y + v*rect.h, -1)
  glActiveTexture(GL_TEXTURE0);
  glBegin(GL_TRIANGLES);
    VERT(0, 0);
    VERT(0, 1);
    VERT(1, 1);

    VERT(0, 0);
    VERT(1, 1);
    VERT(1, 0);
  glEnd();
#undef VERT
  glEnable(GL_CULL_FACE);
}

void ceDeviceGL20::DrawBoundingBox(const ceBoundingBox &bbox)
{
  const ceVector3f& min = bbox.GetMin();
  const ceVector3f& max = bbox.GetMax();

  glBegin (GL_LINES);
    glVertex3f (min.x, min.y, min.z);
    glVertex3f (max.x, min.y, min.z);
    glVertex3f (max.x, min.y, min.z);
    glVertex3f (max.x, min.y, max.z);
    glVertex3f (max.x, min.y, max.z);
    glVertex3f (min.x, min.y, max.z);
    glVertex3f (min.x, min.y, max.z);
    glVertex3f (min.x, min.y, min.z);

    glVertex3f (min.x, max.y, min.z);
    glVertex3f (max.x, max.y, min.z);
    glVertex3f (max.x, max.y, min.z);
    glVertex3f (max.x, max.y, max.z);
    glVertex3f (max.x, max.y, max.z);
    glVertex3f (min.x, max.y, max.z);
    glVertex3f (min.x, max.y, max.z);
    glVertex3f (min.x, max.y, min.z);

    glVertex3f (min.x, min.y, min.z);
    glVertex3f (min.x, max.y, min.z);
    glVertex3f (max.x, min.y, min.z);
    glVertex3f (max.x, max.y, min.z);
    glVertex3f (max.x, min.y, max.z);
    glVertex3f (max.x, max.y, max.z);
    glVertex3f (min.x, min.y, max.z);
    glVertex3f (min.x, max.y, max.z);
  glEnd ();
}

void ceDeviceGL20::SetViewport(const iViewport *viewport)
{
  assert (viewport);
  glViewport (viewport->GetX(), viewport->GetY(), viewport->GetWidth(), viewport->GetHeight());
}

void ceDeviceGL20::SetViewport(const ceRect &viewport)
{
  glViewport (viewport.x, viewport.y, viewport.w, viewport.h);
}

void ceDeviceGL20::SetCamera (const ceCamera &camera)
{
  SetViewMatrix(camera.GetMatrix());
}

void ceDeviceGL20::SetProjector (const ceProjector &projector)
{
  SetProjectionMatrix(projector.GetMatrix());
}

void ceDeviceGL20::Clear(bool clearColor, const ceColor4f &color, bool clearDepth, float depth, bool clearStencil, unsigned stencil)
{
  GLenum cv = 0;
  if (clearColor)
    {
      glClearColor(color.r, color.g, color.b, color.a);
      cv |= GL_COLOR_BUFFER_BIT;
    }
  if (clearDepth)
    {
      glClearDepth(depth);
      cv |= GL_DEPTH_BUFFER_BIT;
    }
  if (clearStencil)
    {
      glClearStencil(stencil);
      cv |= GL_STENCIL_BUFFER_BIT;
    }
  glClear (cv);
}


